package com.gitee.apanlh.util.valid;

import com.gitee.apanlh.exp.IdcardValidException;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.base.builder.MapBuilder;
import com.gitee.apanlh.util.date.DateUtils;
import com.gitee.apanlh.util.date.ZodiacUtils;

import java.util.Map;

/**	
 * 	身份证工具类
 * 
 * 	@author Pan
 */
public class IdcardUtils {
	
	/** 省，直辖市代码表： */
	static final Map<String, String> PROVINCE_MAP = new MapBuilder.Builder<String, String>(35)
		.put("11", "北京").put("12", "天津").put("13", "河北").put("14", "山西").put("15", "内蒙古")
		.put("21", "辽宁").put("22", "吉林").put("23", "黑龙江")
		.put("31", "上海").put("32", "江苏").put("33", "浙江").put("34", "安徽").put("35", "福建").put("36", "江西").put("37", "山东")
		.put("41", "河南").put("42", "湖北").put("43", "湖南").put("44", "广东").put("45", "广西").put("46", "海南")
		.put("50", "重庆").put("51", "四川").put("52", "贵州").put("53", "云南").put("54", "西藏")
		.put("61", "陕西").put("62", "甘肃").put("63", "青海").put("64", "宁夏").put("65", "新疆")
		.put("71", "台湾")
		.put("81", "香港").put("82", "澳门")
		.put("91", "国外")
	.build();
	
	/** 1.第18位校检码 */
	private static final String[] VERIFY_CODE = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
	/**	2.每位加权因子 */
	private static final int[] WEIGHTING = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
	/** 身份证位数 */
	private static final int IDCARD_15LEN = 15;
	private static final int IDCARD_18LEN = 18;
	/** 十五位转换十八位 */
	private static final String CONVERT_YEAR = "19";
	/** 性别 */
	private static final String MAN = "男";
	private static final String WOMAN = "女";
	private static final String UNKNOWN = "未知";
	
	/**
	 * 	默认构造函数
	 * 
	 * 	@author Pan
	 */
	private IdcardUtils() {
		//	不允许外部实例
		super();
	}
	
	/**	
	 * 	校验身份证是否为合法身份证	
	 * 	<br>通用校验15位18位即可
	 * 	
	 * 	@author Pan
	 * 	@param  idCard	身份证号	身份证号
	 * 	@return	boolean
	 */
	public static boolean valid(String idCard) {
		Assert.isNotEmpty(idCard);
		
		if (idCard.length() == 15) {
			return valid15(idCard);
		}
		if (idCard.length() == 18) {
			return valid18(idCard);
		}
		return false;
	}
	
	/**	
	 * 	校验身份证是否为合法身份证	
	 * 	<br>15位身份证
	 * 	
	 * 	@author Pan
	 * 	@param  idCard	身份证号	身份证号
	 * 	@return	boolean
	 */
	public static boolean valid15(String idCard) {
		Assert.isNotEmpty(idCard);
		
		if (idCard.length() == IDCARD_15LEN && RegexUtils.isNumeric(idCard)) {
			idCard15To18(idCard);
			return true;
		}
		return false;
	}
	
	/**	
	 * 	校验身份证是否为合法身份证	
	 * 	<br>18位身份证
	 * 	
	 * 	@author Pan
	 * 	@param  idCard	身份证号	身份证号
	 * 	@return	boolean
	 */
	public static boolean valid18(String idCard) {
		Assert.isNotEmpty(idCard);
		
		int lastIndex = 17;
		return idCard.length() == IDCARD_18LEN && idCard.substring(lastIndex).equals(lastValidCode(idCard.substring(0, lastIndex)));
	}
	
	/**	
	 * 	身份证15位转换18位
	 * 	<br>如果转换失败则抛出{@link IdcardValidException} 异常
	 * 
	 * 	@author Pan
	 * 	@param  idCard	身份证号	身份证号
	 * 	@return	String
	 */
	public static String idCard15To18(String idCard) {
		StringBuilder idCard18 = new StringBuilder(18);
		
		if (idCard.length() != IDCARD_15LEN || !RegexUtils.isNumeric(idCard)) {
			throw new IdcardValidException("idCard15By18长度或数字校验失败!" + idCard);
		}
		
		//	县级
		String city = StringUtils.subStr(idCard, 0, 6);
		//	省级
		String province = PROVINCE_MAP.get(city.substring(0, 2));
		Assert.isNotNullThrows(province, new IdcardValidException(StringUtils.format("idCard15By18省份校验失败:{}", idCard)));

		idCard18.append(city);
		//	日期
		try {
			idCard18.append(CONVERT_YEAR);
			idCard18.append(DateUtils.formatDate(StringUtils.subStr(idCard, 6, 12), "yyMMdd", "yyMMdd"));
		} catch (Exception e) {
			throw new IdcardValidException("idCard15By18日期校验失败!:" + idCard, e);
		}
		
		//	第15位身份证位数
		idCard18.append(StringUtils.subStr(idCard, 12));
		//	加权运算
		idCard18.append(lastValidCode(idCard18));
		return idCard18.toString();
	}
	
	/**	
	 * 	身份证18位转换15位
	 * 	
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	String
	 */
	public static String idCard18To15(String idCard) {
		if (idCard.length() != IDCARD_18LEN || !RegexUtils.isNumeric(idCard)) {
			throw new IdcardValidException("idCard18By15长度或数字校验失败!" + idCard);
		}
		
		String year = getYear(idCard);
		if (year == null || year.startsWith("2")) {
			throw new IdcardValidException(StringUtils.format("出生年月必须小于20年 身份号:[{}]", idCard));
		}
		
		//	校验是否18位身份证是否正确
		if (valid18(idCard)) {
			return new StringBuilder(idCard).replace(6, 8, "").replace(15, 16, "").toString();
		} else {
			throw new IdcardValidException("idCard18By15校验18位身份证真实性失败!" + idCard);
		}
	}
	
	/**
	 * 	返回第十八位校验码	
	 * 
	 * 	@author Pan
	 * 	@param  idCard17	身份证号17	计算后得到的第十七位
	 * 	@return	String
	 */
	public static String lastValidCode(String idCard17) {
		int weightingVal = 0;
		
		for (int i = 0, len = idCard17.length(); i < len; i++) {
			weightingVal += WEIGHTING[i] * (idCard17.charAt(i) - ('0'));
		}
		return VERIFY_CODE[weightingVal % 11];
	}

	/**
	 * 	返回第十八位校验码
	 * 
	 * 	@author Pan
	 * 	@param  idCard17	身份证号17	计算后得到的第十七位
	 * 	@return	String
	 */
	private static String lastValidCode(StringBuilder idCard17) {
		int weightingVal = 0;
		for (int i = 0, len = idCard17.length(); i < len; i++) {
			weightingVal += WEIGHTING[i] * (idCard17.charAt(i) - ('0'));
		}
		return VERIFY_CODE[weightingVal % 11];
	}

	/**	
	 * 	获取身份证中的省份
	 * 
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	String
	 */
	public static String getProvince(String idCard) {
		return valid(idCard) ? PROVINCE_MAP.get(idCard.substring(0, 2)) : null;
	}
	
	/**	
	 * 	获取身份证中的出生年月日
	 * 	<br>yyyy-MM-dd
	 * 
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	String
	 */
	public static String getBirth(String idCard) {
		if (!valid(idCard)) {
			return null;
		}
		if (idCard.length() == IDCARD_15LEN) {
			return DateUtils.formatDate(CONVERT_YEAR.concat(idCard.substring(6, 12)), "yyyyMMdd", "yyyy-MM-dd");
		}
		return DateUtils.formatDate(idCard.substring(6, 14), "yyyyMMdd", "yyyy-MM-dd");
	}
	
	/**	
	 * 	获取身份证中的年
	 * 	
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	String
	 */
	public static String getYear(String idCard) {
		if (!valid(idCard)) {
			return null;
		}
		if (idCard.length() == IDCARD_15LEN) {
			return CONVERT_YEAR.concat(idCard.substring(6, 8));
		}
		return idCard.substring(6, 10);
	}
	
	/**	
	 * 	获取身份证中的月
	 * 
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	String
	 */
	public static String getMonth(String idCard) {
		if (!valid(idCard)) {
			return null;
		}
		if (idCard.length() == IDCARD_15LEN) {
			return idCard.substring(8, 10);
		}
		return idCard.substring(10, 12);
	}
	
	/**	
	 * 	获取身份证中的天
	 * 
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	String
	 */
	public static String getDay(String idCard) {
		if (valid(idCard)) {
			return null;
		}
		if (idCard.length() == IDCARD_15LEN) {
			return idCard.substring(10, 12);
		}
		return idCard.substring(12, 14);
	}
	
	/**	
	 * 	获取身份证中的性别
	 * 	
	 * 	@author Pan
	 * 	@param  idCard	身份证号	
	 * 	@return	String
	 */
	public static String getGender(String idCard) {
		if (valid(idCard)) {
			return UNKNOWN;
		}
		if (idCard.length() == IDCARD_15LEN) {
			return idCard.charAt(IDCARD_15LEN - 1) % 2 == 0 ? WOMAN : MAN;
		} 
		//	第十七位
		return idCard.charAt(IDCARD_18LEN - 2) % 2 == 0 ? WOMAN : MAN;
	}
	
	/**	
	 * 	获取年龄
	 * 	
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	Integer
	 */
	public static Integer getAge(String idCard) {
		String year = getYear(idCard);
		if (year == null) {
			return -1;
		}
		return valid(idCard) ? DateUtils.getCurrentYear() - Integer.parseInt(year, 10) : null;
	}
	
//	/**
//	 * 	获取身份证中的生肖
//	 *
//	 * 	@author Pan
//	 * 	@param  idCard	身份证号
//	 * 	@return	String
//	 */
//	public static String getChineseZodiac(String idCard) {
//		return valid(idCard) ? ChineseDateUtils.getZodiac(Integer.parseInt(getYear(idCard), 10)) : null;
//	}
	
	/**	
	 * 	获取身份证中的西方星座
	 * 
	 * 	@author Pan
	 * 	@param  idCard	身份证号
	 * 	@return	String
	 */
	public static String getZodiac(String idCard) {
		return valid(idCard) ? ZodiacUtils.getChineseZodiac(getBirth(idCard)) : null;
	}
}
